from __future__ import annotations

"""
订单视图路由（orders）
----------------------
职责：
- 普通用户：查看自己的资产快照与近期订单
- 管理员：查看最近的全量订单简表
"""

import datetime as dt
from decimal import Decimal
from fastapi import APIRouter, Depends, HTTPException, Query
from typing import Any, Dict, Optional
from sqlalchemy import select, func, or_, desc, asc
from sqlalchemy.ext.asyncio import AsyncSession

from ..db import get_session
from ..deps import require_admin
from ..models import (
    ExecOrder,
    OrderStatus,
    Script,
    ScriptSignal,
    TradeSide,
    OrderType as OrderTypeEnum,
    PositionAction as PositionActionEnum,
)
from pydantic import EmailStr

from ..services.utils import isoformat
from ..schemas import PlaceOrderRequest, PlaceOrderResponse, TradesQueryRequest, TradesResponse
from ..deps import require_user
from ..services.hb_client import HBClientError, hb_client
from ..config import get_settings
from ..services.utils import default_account_name

router = APIRouter(prefix="/orders", tags=["orders"])


@router.get(
    "/admin/recent",
    summary="管理员查看近期订单列表",
    description=(
        "仅管理员可见。支持按照账户、交易所、交易对、状态等条件过滤，可分页查询与排序，"
        "用于运维与风控排查。"
    ),
)
async def admin_recent_orders(
    admin=Depends(require_admin),
    session: AsyncSession = Depends(get_session),
    page: int = Query(1, ge=1, description="页码，从 1 开始"),
    page_size: int = Query(20, ge=1, le=200, description="每页数量，默认 20，最大 200"),
    account_name: Optional[str] = Query(None, description="按账户名模糊搜索"),
    connector: Optional[str] = Query(None, description="按交易所过滤"),
    symbol: Optional[str] = Query(None, description="按交易对过滤"),
    side: Optional[str] = Query(None, description="按方向过滤（BUY/SELL）"),
    order_type: Optional[str] = Query(None, description="按订单类型过滤（MARKET/LIMIT）"),
    position_action: Optional[str] = Query(None, description="按动作过滤（OPEN/CLOSE）"),
    status: Optional[str] = Query(None, description="按状态过滤"),
    signal_name: Optional[str] = Query(None, description="按信号名称模糊搜索"),
    search: Optional[str] = Query(None, description="关键字搜索（账户/交易所/交易对/信号/TxRef）"),
    sort_by: str = Query("created_at", description="排序字段"),
    sort_order: str = Query("desc", description="排序方向（asc/desc）"),
) -> dict:
    filters = []

    def _like(value: Optional[str]) -> Optional[str]:
        return f"%{value.strip()}%" if value and value.strip() else None

    if account_name and account_name.strip():
        filters.append(ExecOrder.account_name.ilike(_like(account_name)))
    if connector and connector.strip():
        filters.append(ExecOrder.connector.ilike(_like(connector)))
    if symbol and symbol.strip():
        filters.append(ExecOrder.symbol.ilike(_like(symbol.upper())))

    if side:
        try:
            filters.append(ExecOrder.side == TradeSide(side.upper()))
        except ValueError:
            pass
    if order_type:
        try:
            filters.append(ExecOrder.order_type == OrderTypeEnum(order_type.upper()))
        except ValueError:
            pass
    if position_action:
        try:
            filters.append(ExecOrder.position_action == PositionActionEnum(position_action.upper()))
        except ValueError:
            pass
    if status:
        try:
            filters.append(ExecOrder.status == OrderStatus(status.upper()))
        except ValueError:
            pass
    if signal_name and signal_name.strip():
        filters.append(Script.name.ilike(_like(signal_name)))

    if search and search.strip():
        pattern = _like(search)
        filters.append(
            or_(
                ExecOrder.account_name.ilike(pattern),
                ExecOrder.connector.ilike(pattern),
                ExecOrder.symbol.ilike(pattern),
                ExecOrder.tx_ref.ilike(pattern),
                Script.name.ilike(pattern),
            )
        )

    base_query = (
        select(ExecOrder, Script.name.label("script_name"))
        .outerjoin(ScriptSignal, ExecOrder.signal_id == ScriptSignal.id)
        .outerjoin(Script, ScriptSignal.script_id == Script.id)
    )

    sort_map = {
        "created_at": ExecOrder.created_at,
        "qty": ExecOrder.qty,
        "price": ExecOrder.price,
        "status": ExecOrder.status,
        "side": ExecOrder.side,
        "account_name": ExecOrder.account_name,
        "connector": ExecOrder.connector,
        "symbol": ExecOrder.symbol,
        "order_type": ExecOrder.order_type,
        "position_action": ExecOrder.position_action,
        "tx_ref": ExecOrder.tx_ref,
        "signal_name": Script.name,
    }
    sort_key = sort_by.lower() if sort_by else "created_at"
    sort_column = sort_map.get(sort_key, ExecOrder.created_at)
    sort_direction = sort_order.lower() if sort_order else "desc"
    order_clause = desc(sort_column) if sort_direction == "desc" else asc(sort_column)

    offset = (page - 1) * page_size

    stmt = base_query.order_by(order_clause).offset(offset).limit(page_size)
    count_stmt = (
        select(func.count())
        .select_from(ExecOrder)
        .outerjoin(ScriptSignal, ExecOrder.signal_id == ScriptSignal.id)
        .outerjoin(Script, ScriptSignal.script_id == Script.id)
    )

    if filters:
        stmt = stmt.where(*filters)
        count_stmt = count_stmt.where(*filters)

    total_result = await session.execute(count_stmt)
    total = total_result.scalar_one()

    result = await session.execute(stmt)
    rows = result.all()

    def _format_created_at(value: dt.datetime | None) -> str | None:
        iso_str = isoformat(value)
        if not iso_str:
            return None
        try:
            dt_value = dt.datetime.fromisoformat(iso_str)
            return dt_value.strftime("%Y-%m-%d %H:%M:%S")
        except Exception:  # noqa: BLE001
            return iso_str

    def _to_float(value: Optional[Decimal]) -> Optional[float]:
        if value is None:
            return None
        try:
            return float(value)
        except Exception:  # noqa: BLE001
            return None

    items = []
    for order, script_name_value in rows:
        items.append(
            {
                "signal_name": script_name_value,
                "account_name": order.account_name,
                "connector": order.connector,
                "symbol": order.symbol,
                "side": order.side.value,
                "order_type": order.order_type.value,
                "position_action": order.position_action.value,
                "qty": _to_float(order.qty),
                "price": _to_float(order.price),
                "status": order.status.value,
                "error": order.error,
                "tx_ref": order.tx_ref,
                "created_at": _format_created_at(order.created_at),
            }
        )

    return {
        "total": total,
        "page": page,
        "page_size": page_size,
        "sort_by": sort_key,
        "sort_order": sort_direction,
        "items": items,
    }


@router.post(
    "/place",
    response_model=PlaceOrderResponse,
    summary="用户下单（调用 hummingbot-api）",
    description=(
        "仅已绑定目标交易所的用户可用。后端将把请求转发到 hb-api `POST /trading/orders`，"
        "并在本地记录执行订单摘要。枚举值需大写：BUY/SELL、MARKET/LIMIT、OPEN/CLOSE。"
    ),
)
async def place_order(
    payload: PlaceOrderRequest,
    user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> PlaceOrderResponse:
    settings = get_settings()
    account_name = (payload.account_name or default_account_name(user.id, user.email)).strip()

    # 校验用户是否绑定了该交易所
    try:
        connectors = await hb_client.list_account_connectors(account_name)
    except HBClientError as exc:
        raise HTTPException(status_code=502, detail=str(exc)) from exc
    if payload.connector_name not in connectors:
        raise HTTPException(status_code=403, detail="账户未绑定该交易所，无法下单")

    body = {
        "account_name": account_name,
        "connector_name": payload.connector_name,
        "trading_pair": payload.trading_pair,
        "trade_type": payload.trade_type.value,
        "amount": float(payload.amount),
        "order_type": payload.order_type.value,
        "price": float(payload.price or 0),
        "position_action": payload.position_action.value,
    }

    # 调用 hb-api 下单
    try:
        hb_result = await hb_client.place_order(body)
    except HBClientError as exc:
        raise HTTPException(status_code=502, detail=str(exc)) from exc

    # 解析返回中的 id/tx 参考 hb-api 实际结构
    order_id = None
    tx_ref = None
    if isinstance(hb_result, dict):
        order_id = str(hb_result.get("order_id") or hb_result.get("client_order_id") or hb_result.get("id") or "") or None
        tx_ref = str(hb_result.get("tx_ref") or hb_result.get("txId") or "") or None

    # 仅写入执行订单表的最小必要信息
    exec_order = ExecOrder(
        user_id=user.id,
        account_name=account_name,
        connector=payload.connector_name,
        symbol=payload.trading_pair,
        side=payload.trade_type,
        order_type=payload.order_type,
        position_action=payload.position_action,
        qty=payload.amount,
        price=payload.price,
        status=OrderStatus.sent,
        tx_ref=tx_ref,
    )
    session.add(exec_order)
    await session.commit()

    return PlaceOrderResponse(ok=True, order_id=order_id, tx_ref=tx_ref, hb_response=hb_result or {})


@router.post(
    "/trades",
    response_model=Dict[str, Any],
    summary="查询订单历史（透传 hb-api）",
    description=(
        "将请求体转发到 hummingbot-api `POST /trading/orders/search`，默认使用当前用户邮箱作为 `account_names`。"
        "可按 limit/start_time/end_time/cursor/connector_names/trading_pairs/statuses/trade_types 过滤。"
    ),
)
async def get_trades(
    payload: TradesQueryRequest,
    user=Depends(require_user),
) -> Dict[str, Any]:
    body = payload.model_dump()
    account_names = body.get("account_names")
    normalized_accounts: list[str]
    if not account_names:
        normalized_accounts = [user.email]
    elif isinstance(account_names, (str, EmailStr)):
        normalized_accounts = [str(account_names)]
    else:
        normalized_accounts = [str(name) for name in account_names]

    user_account_lower = user.email.lower()
    for name in normalized_accounts:
        if name.lower() != user_account_lower:
            raise HTTPException(status_code=403, detail="不允许查询其他账户的订单记录")
    body["account_names"] = normalized_accounts

    # 兼容大小写（上游接口通常大小写不敏感，但先统一为大写方便调用方阅读）
    body["trading_pairs"] = [pair.upper() for pair in body.get("trading_pairs", [])]
    body["trade_types"] = [t.upper() for t in body.get("trade_types", [])]
    body["statuses"] = [s.upper() for s in body.get("statuses", [])]
    try:
        result = await hb_client.search_orders(body)
    except HBClientError as exc:
        raise HTTPException(status_code=502, detail=str(exc)) from exc
    # 将时间字段转换为 yyyy-mm-dd H:M:S（若存在）
    def _format_time(value: Any) -> Any:
        if isinstance(value, str) and value:
            try:
                dt_value = dt.datetime.fromisoformat(value.replace("Z", "+00:00"))
                dt_cn = dt_value.astimezone(dt.timezone(dt.timedelta(hours=8)))
                return dt_cn.strftime("%Y-%m-%d %H:%M:%S")
            except Exception:  # noqa: BLE001
                return value
        return value

    def _transform(obj: Any) -> Any:
        if isinstance(obj, list):
            return [_transform(item) for item in obj]
        if isinstance(obj, dict):
            new_dict: Dict[str, Any] = {}
            for key, value in obj.items():
                if key in {"created_at", "updated_at", "timestamp", "createdAt", "updatedAt"}:
                    new_dict[key] = _format_time(value)
                else:
                    new_dict[key] = _transform(value)
            return new_dict
        return obj

    return _transform(result)
